﻿using System;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Diagnostics;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;

namespace QExplorer
{
	public struct Theme
	{
		public string name;
		public Color dark;
		public Color light;
		public Theme(string sname, Color cdark, Color clight)
		{
			name = sname;
			dark = cdark;
			light = clight;
		}
	}
	public partial class QForm : Form
	{
		#region Fields

		public System.Resources.ResourceManager rm = 
			new System.Resources.ResourceManager("QExplorer.Properties.Resources",
				typeof(QForm).Assembly);

		[Serializable]
		private struct ViewData
		{
			public string pwd;
			public int sortColumn;
			public View viewType;
		}
		[Serializable]
		private struct FavData
		{
			public string text;
			public string path;
			public int weight;
		}

		enum Views
		{
			Icon,
			Details,
			Count
		}

		public string Status
		{
			get { return statusLabel1.Text; }
			set { statusLabel1.Text = value; }
		}

		public QView CurView
		{
			get { return (QView)(tabCtrl.SelectedTab.Controls[0]); }
		}
		private IntPtr sysLargeImageList;
		private IntPtr sysSmallImageList;

		private Font textBoxAddrFont;

		public bool WatcherEnabled
		{
			get { return watcher.EnableRaisingEvents; }
			set { watcher.EnableRaisingEvents = value; }
		}
		public Theme[] themes;


		#endregion

		#region Form
		public QForm()
		{
			try
			{
				System.Threading.Thread.CurrentThread.CurrentUICulture =
					new System.Globalization.CultureInfo(Properties.Settings.Default.Language);
			}
			catch (System.Exception ex)
			{
				MessageBox.Show(ex.Message);
			}
			InitializeComponent();
			themes = new Theme[10];
			themes[0] = new Theme("Blue", Color.Blue, Color.LightBlue);
			themes[1] = new Theme("Green", Color.Green, Color.LightGreen);
			themes[2] = new Theme("Gold", Color.Gold, Color.Yellow);
			themes[3] = new Theme("Gray", Color.DarkGray, Color.Gray);
			themes[4] = new Theme("Red", Color.Red, Color.Pink);
			themes[5] = new Theme("Black", Color.Black, Color.FloralWhite);

		}

		private void SetTheme(int index)
		{
			SetTheme(themes[index].dark, themes[index].light);
		}
		private void SetTheme(Color dark, Color light)
		{
			toolBar.BackgroundImage = CreateVbkImage(toolBar.Height, dark, light);
			bookmarkBar.BackgroundImage = CreateVbkImage(bookmarkBar.Height, dark, light);
			statusBar.BackgroundImage = CreateVbkImage(statusBar.Height, Color.Snow, dark);
			spliterMain.BackColor = light;
		}

		private Bitmap CreateVbkImage(int height, Color topColor, Color bottomColor)
		{
			Bitmap bmp = new Bitmap(1, height);
			for (int i = 0; i < height; i++)
			{
				int r = i * bottomColor.R / height + (height - i) * topColor.R / height;
				int g = i * bottomColor.G / height + (height - i) * topColor.G / height;
				int b = i * bottomColor.B / height + (height - i) * topColor.B / height;
				bmp.SetPixel(0, i, Color.FromArgb(r, g, b));
			}
			return bmp;
		}

		private void QForm_Load(object sender, EventArgs e)
		{
			
			favView.Form = this;
			tree.Form = this;
			bookmarkBar.Form = this;
			textBoxAddrFont = textBoxAddr.Font;
			SetTheme(Properties.Settings.Default.CurTheme);

			// Get system image list
			SHFILEINFO sfi = new SHFILEINFO();
			sysLargeImageList = API.GetFileInfo(@"C:\", ref sfi,
				 API.SHGFI_LARGEICON | API.SHGFI_SYSICONINDEX);
			sysSmallImageList = API.GetFileInfo(@"c:\", ref sfi,
				API.SHGFI_SMALLICON | API.SHGFI_SYSICONINDEX);
			IntPtr sysStateImageList = API.GetFileInfo(@"c:\", ref sfi,
				API.SHGFI_OPENICON | API.SHGFI_SYSICONINDEX);

			API.SendMessage(favView.Handle, API.LVM_SETIMAGELIST,
				(IntPtr)API.LVSIL_NORMAL, sysLargeImageList);

			API.SendMessage(tree.Handle, API.TVM_SETIMAGELIST,
				(IntPtr)API.TVSIL_NORMAL, sysSmallImageList);
			API.SendMessage(tree.Handle, API.TVM_SETIMAGELIST,
				(IntPtr)API.TTVSIL_STATE, sysStateImageList);

			tree.SetTree();

			try
			{
				LoadSettings();
			}
			catch (System.Exception ex)
			{
				Debug.WriteLine(ex.ToString());
				NewTab(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), (int)Column.Type);
			}
		}
		private void QForm_FormClosing(object sender, FormClosingEventArgs e)
		{
			SaveSettings();
		}

		#endregion

		#region Config
		public void SaveView(string filename)
		{
			int n = tabCtrl.Controls.Count;
			ViewData[] views = new ViewData[n];
			for (int i = 0; i < n; i++)
			{
				QView v = (QView)(tabCtrl.Controls[i].Controls[0]);
				views[i].pwd = v.Pwd;
				views[i].sortColumn = v.SortColIndex;
				views[i].viewType = v.View;
			}
			FavData[] favorites = new FavData[favView.Items.Count];
			for (int i = 0; i < favView.Items.Count; i++)
			{
				ListViewItem item = favView.Items[i];
				favorites[i].text = item.Text;
				favorites[i].path = item.Name;
				favorites[i].weight = (int)item.Tag;
			}

			IFormatter matter = new BinaryFormatter();
			FileStream file = new FileStream(filename, FileMode.Create, FileAccess.Write);
			matter.Serialize(file, views);
			matter.Serialize(file, tabCtrl.SelectedIndex);
			matter.Serialize(file, favorites);
			file.Close();
		}

		public void LoadView(string filename)
		{
			IFormatter matter = new BinaryFormatter();
			FileStream file = new FileStream(filename, FileMode.Open, FileAccess.Read);
			ViewData[] views = (ViewData[])matter.Deserialize(file);
			int curIndex = (int)matter.Deserialize(file);
			FavData[] favorites = (FavData[])matter.Deserialize(file);
			file.Close();

			foreach (ViewData view in views)
			{
				NewTab(view.pwd, view.sortColumn);
				CurView.View = view.viewType;
			}
			SHFILEINFO sfi = new SHFILEINFO();
			foreach (FavData fav in favorites)
			{
				ListViewItem item = new ListViewItem(fav.text);
				item.Name = fav.path;
				API.GetFileInfo(item.Name, ref sfi, API.SHGFI_SYSICONINDEX);
				item.ImageIndex = sfi.iIcon;
				item.Tag = fav.weight;
				item.ToolTipText = fav.path + "\n" + fav.weight.ToString();
				favView.Items.Add(item);
			}
			tabCtrl.SelectTab(curIndex);
			OnActiveView();
		}

		private void LoadSettings()
		{
			Properties.Settings sett = Properties.Settings.Default;
			foreach (string bookmark in sett.Bookmarks)
			{
				bookmarkBar.AddBookmark(bookmark);
			}
			minimizeToTrayToolStripMenuItem.Checked = sett.MinimizeToTray;
			LoadView(sett.ViewFile);
			WindowState = sett.Maximized ? FormWindowState.Maximized : FormWindowState.Normal;
			if (!sett.Maximized)
			{
				Location = sett.Location;
				Size = sett.Size;
			}
			spliterMain.SplitterDistance = sett.SpliterMainDistance;
			spliterLeft.SplitterDistance = sett.SpliterLeftDistance;
		}

		private void SaveSettings()
		{
			Properties.Settings sett = Properties.Settings.Default;
			sett.Bookmarks = new System.Collections.Specialized.StringCollection();
			foreach (ToolStripItem btn in bookmarkBar.Items)
			{
				sett.Bookmarks.Add((string)btn.Tag);
			}
			if (WindowState == FormWindowState.Normal)
			{
				sett.Location = Location;
				sett.Size = Size;
			}
			sett.Maximized = WindowState == FormWindowState.Maximized;
			SaveView("default.view");
			sett.SpliterMainDistance = spliterMain.SplitterDistance;
			sett.SpliterLeftDistance = spliterLeft.SplitterDistance;
			sett.Save();
		}
		#endregion

		private void NewTab(string dir, int sortCol)
		{
			TabPage tab = new TabPage(GetDirName(dir));
			tabCtrl.TabPages.Add(tab);
			tab.AllowDrop = true;

			QView v = new QView((QForm)FindForm());
			tab.Controls.Add(v);

			v.SortColIndex = sortCol;
			v.SetImageList(sysLargeImageList, sysSmallImageList);
			v.OpenDir(dir);
		}

		private string GetDirName(string dir)
		{
			// 			Debug.Assert(Directory.Exists(dir));
			string d = Path.GetFileName(dir);
			return d == "" ? dir : d;
		}

		public void OnActiveView()
		{
			try
			{
				QView v = CurView;
				buttonUp.Enabled = Directory.GetParent(v.Pwd) != null;
				TabPage tab = (TabPage)(v.Parent);
				tab.Text = GetDirName(v.Pwd);
				tab.ToolTipText = v.Pwd;
				textBoxAddr.Text = v.Pwd;
				switch (CurView.View)
				{
				case View.LargeIcon:
					buttonView.Image = Properties.Resources.ViewIcons;
					break;
				case View.Details:
					buttonView.Image = Properties.Resources.ViewDetails;
					break;
				}
				TreeNode[] nodes = null;
				string temp = CurView.Pwd.Trim('\\', '/');
				string[] parents = temp.Split('\\', '/');
				string root = Directory.GetDirectoryRoot(CurView.Pwd);
				TreeNodeCollection tnodes = tree.Nodes;
				for (int i = 1; i < parents.Length; i++)
				{
					nodes = tnodes.Find(root, false);
					nodes[0].Expand();
					root = Path.Combine(root, parents[i]);
					tnodes = nodes[0].Nodes;
				}
				if (nodes != null)
				{
					nodes = tnodes.Find(root, false);
					nodes[0].EnsureVisible();
					tree.SelectedNode = nodes[0];
				}
				buttonBack.Enabled = CurView.History.Count > CurView.PosInHistory;
				buttonForward.Enabled = CurView.PosInHistory != 0;
				watcher.Path = v.Pwd;
			}
			catch (System.Exception ex)
			{
				MessageBox.Show(ex.ToString());
			}
		}

		private bool ShowFile(FileInfo file)
		{
			return (file.Attributes & FileAttributes.Hidden) == 0
				&& (file.Attributes & FileAttributes.System) == 0;
		}

		/// <summary>
		/// Open a folder in current view or run it if it is a file
		/// </summary>
		/// <param name="path">folder or file's fullname</param>
		public void Open(string path)
		{
			try
			{
				if (Directory.Exists(path))
				{
					CurView.OpenDir(path);
					OnActiveView();
				}
				else
				{
					Process.Start(path);
				}
				favView.AddItem(path);
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, this.Text, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		#region ToolStrip

		private void buttonBack_Click(object sender, EventArgs e)
		{
			CurView.OpenDirInHistory(CurView.History[CurView.PosInHistory++]);
			OnActiveView();
		}

		private void buttonForward_Click(object sender, EventArgs e)
		{
			CurView.OpenDirInHistory(CurView.History[CurView.PosInHistory - 1]);
			CurView.PosInHistory--;
			OnActiveView();
		}

		private void buttonUp_Click(object sender, EventArgs e)
		{
			DirectoryInfo parent = Directory.GetParent(CurView.Pwd);
			if (parent != null)
			{
				CurView.OpenDir(parent.FullName);
				OnActiveView();
			}
		}

		private void btnFavorite_Click(object sender, EventArgs e)
		{
			ToolStripButton btn = (ToolStripButton)sender;
			if (btn.Tag != null)
			{
				Open((string)btn.Tag);
			}
		}

		public void CreateFoloder()
		{
			string initName = "NewFolder";
			QView v = CurView;
			string folderName = initName;
			try
			{
				int i;
				for (i = 1; i < 65536; )
				{
					if (Directory.Exists(Path.Combine(v.Pwd, folderName)))
					{
						folderName = initName + (++i).ToString();
					}
					else
					{
						string path = Path.Combine(v.Pwd, folderName);
						Directory.CreateDirectory(path);
						// 						v.AddItem(path);
						// 						ListViewItem item = v.FindItemWithText(initName + i.ToString());
						// 						item.BeginEdit();
						break;
					}
				}
			}
			catch (System.Exception ex)
			{
				MessageBox.Show(ex.ToString());
			}
		}

		private void buttonNewFolder_Click(object sender, EventArgs e)
		{
			CreateFoloder();
		}

		private void textBoxAddr_MouseMove(object sender, MouseEventArgs e)
		{
			if (e.Button != MouseButtons.None)
			{
				return;
			}
			int oldSelLength = textBoxAddr.SelectionLength;
			if (oldSelLength > 0)
			{
				textBoxAddr.Cursor = Cursors.IBeam;
				return;
			}
			int oldSel = textBoxAddr.SelectionStart;

			textBoxAddr.SelectAll();
			this.textBoxAddr.SelectionFont = textBoxAddrFont;
			textBoxAddr.SelectionBackColor = Color.White;
			textBoxAddr.SelectionColor = Color.Gray;

			int i = textBoxAddr.GetCharIndexFromPosition(e.Location);
			if (i == textBoxAddr.Text.Length - 1)
			{
				textBoxAddr.Cursor = Cursors.IBeam;
			}
			else
			{
				char[] separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
				int pathEnd = textBoxAddr.Text.IndexOfAny(separators, i);
				if (pathEnd == -1)
				{
					pathEnd = textBoxAddr.TextLength;
				}
				textBoxAddr.Select(0, pathEnd);
				textBoxAddr.SelectionFont = new Font(textBoxAddrFont, FontStyle.Underline);
				textBoxAddr.SelectionColor = Color.Black;
				textBoxAddr.Cursor = Cursors.Hand;
			}
			textBoxAddr.Select(oldSel, oldSelLength);
		}

		private void textBoxAddr_Enter(object sender, EventArgs e)
		{
			int oldStart = textBoxAddr.SelectionStart;
			textBoxAddr.SelectAll();
			textBoxAddr.SelectionColor = Color.Black;
			textBoxAddr.Select(oldStart, 0);
		}

		private void textBoxAddr_KeyPress(object sender, KeyPressEventArgs e)
		{
			if (e.KeyChar == '\r')
			{
				Open(textBoxAddr.Text);
			}
		}

		private void textBoxAddr_Click(object sender, EventArgs e)
		{
			char[] separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
			int pathEnd = textBoxAddr.Text.IndexOfAny(separators, textBoxAddr.SelectionStart);
			if (pathEnd > 0)
			{
				Open(textBoxAddr.Text.Substring(0, pathEnd) + "\\");
			}
			ResetAddrStyle();
		}
		private void ResetAddrStyle()
		{
			int oldSelStart = textBoxAddr.SelectionStart;
			int oldSelLength = textBoxAddr.SelectionLength;
			textBoxAddr.SelectAll();
			textBoxAddr.SelectionFont = textBoxAddrFont;
			textBoxAddr.SelectionBackColor = Color.White;
			textBoxAddr.SelectionColor = Color.Gray;
			textBoxAddr.Select(oldSelStart, oldSelLength);
		}

		private void textBoxAddr_MouseLeave(object sender, EventArgs e)
		{
			ResetAddrStyle();
		}

		private void buttonTreeView_Click(object sender, EventArgs e)
		{
			spliterMain.Panel1Collapsed = !spliterMain.Panel1Collapsed;
		}

		private void buttonTreeView_MouseHover(object sender, EventArgs e)
		{
			string s = spliterMain.Panel1Collapsed ? rm.GetString("ShowFolderView"): rm.GetString("HideFolderView");
			toolTip1.SetToolTip(buttonTreeView, s);
		}

		private void buttonView_Click(object sender, EventArgs e)
		{
			switch (CurView.View)
			{
			case View.LargeIcon:
				CurView.View = View.Details;
				buttonView.Image = Properties.Resources.ViewDetails;
				break;
			case View.Details:
				CurView.View = View.LargeIcon;
				buttonView.Image = Properties.Resources.ViewIcons;
				break;
			}
		}

		private void toolButton_MouseEnter(object sender, EventArgs e)
		{
			VistaButtonTest.VistaButton btn = (VistaButtonTest.VistaButton)sender;
			btn.ImageSize = new Size(24, 24);
		}

		private void toolButton_MouseLeave(object sender, EventArgs e)
		{
			VistaButtonTest.VistaButton btn = (VistaButtonTest.VistaButton)sender;
			btn.ImageSize = new Size(20, 20);
		}
		#endregion
		#region Menu
		private void buttonOption_Click(object sender, EventArgs e)
		{
			Debug.WriteLine("menu= " + menuQ.Width);
			menuQ.Show((Control)sender,
				new Point(((Control)sender).Width - menuQ.Width, toolBar.Height),
				ToolStripDropDownDirection.Right);
		}

		private void languageToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SaveSettings();
			Properties.Settings.Default.Language = (string)((ToolStripMenuItem)sender).Tag;
			Controls.Clear();
			System.Threading.Thread.CurrentThread.CurrentUICulture =
				new System.Globalization.CultureInfo(Properties.Settings.Default.Language);
			InitializeComponent();
			QForm_Load(this, new EventArgs());
			// 			((ToolStripMenuItem)sender).Checked = true;
		}

		private void languageToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
		{
			ToolStripMenuItem menuItem = (ToolStripMenuItem)sender;
			foreach (ToolStripMenuItem item in menuItem.DropDownItems)
			{
				item.Checked = (string)item.Tag == Properties.Settings.Default.Language;
			}
		}

		private void hideKnownExtToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Properties.Settings.Default.HideKnownExt = ((ToolStripMenuItem)sender).Checked;
			Reflesh();
		}

		private void menuQ_Opening(object sender, System.ComponentModel.CancelEventArgs e)
		{
			hideKnownExtToolStripMenuItem.Checked = Properties.Settings.Default.HideKnownExt;
		}
		public void Reflesh()
		{
			CurView.OpenDir(CurView.Pwd);
			OnActiveView();
		}


		#endregion

		#region tabCtrl
		private void UpdateNewTabPosition()
		{
			int tabsWidth = 0;
			for (int i = 0; i < tabCtrl.Controls.Count; i++)
			{
				tabsWidth += tabCtrl.GetTabRect(i).Width;
			}
			buttonNewTab.Location = new Point(tabsWidth + 4, toolBar.Height);

		}

		private void buttonNewTab_Click(object sender, EventArgs e)
		{
			NewTab(CurView.Pwd, CurView.SortColIndex);
			tabCtrl.SelectedIndex = tabCtrl.TabPages.Count - 1;
		}

		private void tabCtrl_Layout(object sender, LayoutEventArgs e)
		{
			UpdateNewTabPosition();
		}
		private void tabCtrl_DoubleClick(object sender, EventArgs e)
		{
			if (tabCtrl.Controls.Count > 1)
			{
				tabCtrl.Controls.Remove(tabCtrl.SelectedTab);
			}
		}

		private void tabCtrl_SelectedIndexChanged(object sender, EventArgs e)
		{
			Reflesh();
		}

		private void tabCtrl_DragEnter(object sender, DragEventArgs e)
		{
			e.Effect = e.AllowedEffect;
		}

		private void tabCtrl_DragDrop(object sender, DragEventArgs e)
		{
			Point targetPoint = tabCtrl.PointToClient(new Point(e.X, e.Y));
			for (int i = 0; i < tabCtrl.Controls.Count; i++)
			{
				if (tabCtrl.GetTabRect(i).Contains(targetPoint))
				{
					TabPage tab = (TabPage)tabCtrl.Controls[i];
					string path = (string)e.Data.GetData(typeof(string));
					try
					{
						string newPath = Path.Combine(tab.ToolTipText, Path.GetFileName(path));
						Directory.Move(path, newPath);
					}
					catch (IOException ex)
					{
						MessageBox.Show(ex.Message);
					}
					break;
				}
			}
		}

		private void splitContainer1_Panel1_Resize(object sender, EventArgs e)
		{
			UpdateNewTabPosition();
		}

		#endregion

		#region fileSystemWatcher
		private void watcher_Deleted(object sender, FileSystemEventArgs e)
		{
			CurView.Items.Remove(CurView.FindItemWithText(e.Name));
		}

		private void watcher_Created(object sender, FileSystemEventArgs e)
		{
			CurView.AddItem(e.FullPath);
		}

		private void watcher_Changed(object sender, FileSystemEventArgs e)
		{
			CurView.Items.Remove(CurView.FindItemWithText(e.Name));
			CurView.AddItem(e.FullPath);
		}

		private void watcher_Renamed(object sender, RenamedEventArgs e)
		{
			ListViewItem item = CurView.FindItemWithText(e.OldName);
			item.Name = e.Name;
		}
		#endregion

		private void themeMenuItem_Click(object sender, EventArgs e)
		{
			for (int i = 0; i < themes.Length; i++)
			{
				if (((ToolStripMenuItem)sender).Text == themes[i].name)
				{
					Properties.Settings.Default.CurTheme = i;
					SetTheme(i);
				}
			}
		}

		private void themeToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
		{
			foreach (ToolStripMenuItem item in themeToolStripMenuItem.DropDownItems)
			{
				item.Checked = item.Text == themes[Properties.Settings.Default.CurTheme].name;
			}
		}


		public bool CanShowFile(string path)
		{
			if ((File.GetAttributes(path) & FileAttributes.Hidden) != 0)
			{
				return false;
			}
			else
			{
				return true;
			}
		}

		private void contextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
		{

		}

		private void minimizeToTrayToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Properties.Settings.Default.MinimizeToTray = ((ToolStripMenuItem)sender).Checked;
		}

		private void QForm_Resize(object sender, EventArgs e)
		{
			if (this.WindowState == FormWindowState.Minimized)
			{
				if (Properties.Settings.Default.MinimizeToTray)
				{
					this.Hide();
					notifyIcon1.Visible = true;
				}
			}
			else
			{
				Properties.Settings.Default.Maximized = WindowState == FormWindowState.Maximized;
			}
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void qExplorerToolStripMenuItem_Click(object sender, EventArgs e)
		{
			this.Show();
			WindowState = Properties.Settings.Default.Maximized ? 
				FormWindowState.Maximized : FormWindowState.Normal;
		}

		private void notifyIcon1_DoubleClick(object sender, EventArgs e)
		{
			this.Show();
			WindowState = Properties.Settings.Default.Maximized ?
				FormWindowState.Maximized : FormWindowState.Normal;
		}



	}


}
